package com.tangxz.generate_code.generate;

import com.tangxz.generate_code.pojo.DataBaseVo;
import com.tangxz.generate_code.pojo.TableVo;
import com.tangxz.generate_code.pojo.TemplateVo;
import com.tangxz.generate_code.util.DataBaseUtil;
import com.tangxz.generate_code.util.FieldUtil;
import com.tangxz.generate_code.util.TemplateUtil;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.util.List;
import java.util.Map;


@Slf4j
public class CodeGenerate {
    //模板配置及基本属性
    private TemplateVo templateVo;
    //数据库配置及基本属性
    private DataBaseVo databaseVo;

    /**
     * 通过表信息和配置文件地址来实生成代码
     *
     * @param generateConfigPath   代码生成器配置  相对于resources的地址，不带后缀
     * @param dataSourceConfigPath 数据库  相对于resources的地址，不带后缀
     */
    public CodeGenerate(String generateConfigPath, String dataSourceConfigPath) {
        this.templateVo = new TemplateVo(generateConfigPath);
        this.databaseVo = new DataBaseVo(dataSourceConfigPath);
    }

    public static void generateManyCodeByConfig(List<TableVo> tableVoList){
        //1、解析数据库配置文件
        DataBaseVo dataBaseVo = new DataBaseVo("tangxz/database");
        //2、解析模板配置文件
        TemplateVo templateVo = new TemplateVo("tangxz/config");
        //3、实例化代码生成器
        CodeGenerate codeGenerate = new CodeGenerate(templateVo, dataBaseVo);
        //4、根据需要生产的表生成代码
        codeGenerate.GeneratingFile(tableVoList);
    }

    /**
     * @param templateVo 模板配置
     * @param databaseVo 数据库配置
     */
    public CodeGenerate(TemplateVo templateVo, DataBaseVo databaseVo) {
        this.templateVo = templateVo;
        this.databaseVo = databaseVo;
    }

    /**
     * 得到表信息，通过表信息和模板，来生成文件。可能为多表同时生成
     * 生成文件
     */
    public void GeneratingFile(TableVo... tableVo) {
        this.databaseVo.setTableInfos(DataBaseUtil.getTableInfos(tableVo, templateVo, databaseVo));
        log.info("--- 文件输出目录为：" + templateVo.getProjectPath());
        for (Map<String, Object> tableInfo : databaseVo.getTableInfos()) {
            for (File template : templateVo.getTemplates()) {
                this.GeneratingFile(template, tableInfo);
            }
        }
    }

    /**
     * 得到表信息，通过表信息和模板，来生成文件。可能为多表同时生成
     * 生成文件
     */
    public void GeneratingFile(List<TableVo> tableVoList) {
        TableVo[] tableVo = new TableVo[tableVoList.size()];
        int i=0;
        for (TableVo vo : tableVoList) {
            tableVo[i++] = vo;
        }
        this.databaseVo.setTableInfos(DataBaseUtil.getTableInfos(tableVo, templateVo, databaseVo));
        log.info("--- 文件输出目录为：" + templateVo.getProjectPath());
        for (Map<String, Object> tableInfo : databaseVo.getTableInfos()) {
            for (File template : templateVo.getTemplates()) {
                this.GeneratingFile(template, tableInfo);
            }
        }
    }

    /**
     * 生成文件
     *
     * @param templateFile 此次生成的文件
     */
    protected void GeneratingFile(File templateFile, Map<String, Object> tableInfo) {
        //得到需要生成文件在模板根目录中的相对路径，java\${bussiPackage}\${entityPackage}\controller\${entityName}Controller.javai
        String templatePath = TemplateUtil.getRelativePath(templateVo.getTemplateRootDir(), templateFile);
        try {
            // 得到生成的文件的类路径
            String filePath = TemplateUtil.getTemplatePath(templatePath, tableInfo, templateVo.getConfiguration());
            //生成文件的全地址（相对路径，相对于项目根目录）
            filePath = templateVo.getSourceRootPackage() + filePath;
            //得到需要保存到的磁盘中的绝对路径
            if (templateVo.getProjectPath() != null) {
                filePath = templateVo.getProjectPath() + File.separator + filePath;
            }
            log.info("--- 生成文件：" + filePath);
            //生成文件
            this.doGeneratingFile(filePath, templatePath, tableInfo);
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
    }

    /**
     * 生成一个文件
     *
     * @param filePath     当前生成文件的全路径       E:\eclipse2018-workspace\jeecg-boot\src/main/java\org\jeecg\modules\demo\demo\controller\DemoController.javai
     * @param templatePath 当前生成文件的模板的相对路径 java\${bussiPackage}\${entityPackage}\controller\${entityName}Controller.javai
     */
    protected void doGeneratingFile(String filePath, String templatePath, Map<String, Object> tableInfo) {
        //创建当前的一个模板文件实例
        //当前template是根据最初初始化的configuration来的，其中配置了模板的根目录，所以这里只需要相对路径即可
        try {
            Template template = templateVo.getConfiguration().getTemplate(templatePath);
            //创建文件路径并返回该文件的File对象
            File nowFile = TemplateUtil.initFilePath(filePath);
            log.info("[com.tangxz.com.tangxz.generate_code.generate]\t template:" + templatePath + " ==> " + filePath);
            //生成文件
            GeneratingFile(template, nowFile, tableInfo);
        } catch (IOException e) {
            e.printStackTrace();
        }
        log.info("--- 生成成功：" + filePath);
    }

    /**
     * @param template 当前的模板
     * @param nowFile  当前生成的文件
     */
    public void GeneratingFile(Template template, File nowFile, Map<String, Object> tableInfo) {
        try {
            //此行仅生成文件
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(nowFile), FieldUtil.encoding));
            //通过template模板，把tableInfo的内容在模板中修改后，写入bw流中
            template.process(tableInfo, bw);
            bw.close();
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
    }


}
